package com.xzcs.util.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.*;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.BooleanQuery.Builder;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class LuceneUtils {

	/**
	 * 将字符串分词
	 * @param str
	 * @return
     */
	public static String analyzerString(String str){
		Analyzer analyzer = new IKAnalyzer(true);
		TokenStream stream = null;
		String result = "";
		try {
			stream = analyzer.tokenStream("field", str);
			stream.reset();
			CharTermAttribute offsetAtt = stream.addAttribute(CharTermAttribute.class);
			while (stream.incrementToken()) {
				result = result + "|" + offsetAtt.toString();
			}
			stream.end();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				stream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (!result.equals("")) {
			result = result.substring(1);
		}
		return result;
	}

	/**
	 * 创建doc对象
	 * @param map
	 * @return
     */
	public static Document createDocument(Map<String, String> map){
		Document doc = new Document();
		Set<Entry<String, String>> set = map.entrySet();
		for (Entry<String, String> entry : set) {
			FieldType fieldType = new FieldType();
			fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);//文档 词频 位置都被索引
	        fieldType.setStored(true);//set 是否存储  
	        fieldType.setTokenized(true);//set 是否分词
			doc.add(new Field(entry.getKey(), entry.getValue(), fieldType));
			//doc.add(new TextField(entry.getKey(), entry.getValue(), Field.Store.YES));
		}
		return doc;
	}

	/**
	 * 创建索引
	 * @param doc
	 * @param indexPath 索引路径
	 * @param isCreate 是否清空重建
     */
	public static void createIndex(Document doc, String indexPath, boolean isCreate){
		Analyzer analyzer = new IKAnalyzer(true);//智能分词
		IndexWriter iwriter = null;
		Directory directory = null;
	    // Store the index in memory:new RAMDirectory()
	    try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			IndexWriterConfig config = new IndexWriterConfig(analyzer);
			//APPEND：总是追加，可能会导致错误，索引还会重复，导致返回多次结果
			//CREATE：清空重建（推荐）
			//CREATE_OR_APPEND【默认】：创建或追加
			if (isCreate) {
				config.setOpenMode(OpenMode.CREATE);
			} else {
				config.setOpenMode(OpenMode.CREATE_OR_APPEND);
			}
			//设置索引维护的方式，追加索引 create是覆盖索引
			iwriter = new IndexWriter(directory, config);
			iwriter.addDocument(doc);
			iwriter.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != iwriter) {
					iwriter.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 删除全部索引
	 * @param indexPath
     */
	public static void deleteAllIndex(String indexPath){
		Analyzer analyzer = new IKAnalyzer(true);
		IndexWriter iwriter = null;
		Directory directory = null;
		try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			IndexWriterConfig config = new IndexWriterConfig(analyzer);
			config.setOpenMode(OpenMode.CREATE_OR_APPEND);
			iwriter = new IndexWriter(directory, config);
			iwriter.deleteAll();
			iwriter.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != iwriter) {
					iwriter.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 删除单个索引
	 * @param key Field的key
	 * @param value Field的值
	 * @param indexPath
     */
	public static void deleteIndex(String key, String value, String indexPath){
		Analyzer analyzer = new IKAnalyzer(true);
		IndexWriter iwriter = null;
		Directory directory = null;
		try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			IndexWriterConfig config = new IndexWriterConfig(analyzer);
			config.setOpenMode(OpenMode.CREATE_OR_APPEND);
			iwriter = new IndexWriter(directory, config);
			iwriter.deleteDocuments(new Term(key, value));
			iwriter.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != iwriter) {
					iwriter.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 创建查询
	 * 分词器和创建索引的分词器要一致
	 * @param map
	 * @return
     */
	public static Query createQuery(Map<String, String> map){
		//Analyzer analyzer = new StandardAnalyzer();
		Analyzer analyzer = new IKAnalyzer(true);
		
		/*String[] queries = {"中文版", "8*"};
		String[] fields = {"name", "isbn"};
		BooleanClause.Occur[] clauses = {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };//或的关系
		Query query = MultiFieldQueryParser.parse(queries, fields, clauses, analyzer);  */
		
		Builder booleanBuilder = new Builder();
		
		try {
			Set<Entry<String, String>> set = map.entrySet();
			// 循环多个条件
			for (Entry<String, String> entry : set) {
				QueryParser parser = new QueryParser(entry.getKey(), analyzer);
				parser.setDefaultOperator(QueryParser.OR_OPERATOR);
				Query query = parser.parse(entry.getValue());

				BooleanClause clause = new BooleanClause(query, BooleanClause.Occur.SHOULD);//每个条件是或的关系 MUST且
				booleanBuilder.add(clause);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return booleanBuilder.build();
	}

	/**
	 * 查询结果
	 * @param query
	 * @param searchSize
	 * @param indexPath
     * @return
     */
	public static TopDocs searchDocs(Query query, int searchSize, String indexPath){
		DirectoryReader ireader = null;
		Directory directory = null;
		TopDocs topDoc = null;
		try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			ireader = DirectoryReader.open(directory);
			IndexSearcher isearcher = new IndexSearcher(ireader);
			/*Sort sort = new Sort(new SortField[]{
					//如果两个都没有注释，那么就是按照先按照相关度，然后按照id排序（相关度相同的情况下）
					SortField.FIELD_SCORE,//注释掉这个就是按照id排序
					new SortField("id", SortField.Type.DOC, false)//注释掉这个就是按照相关度排序
			});*/
			topDoc = isearcher.search(query, searchSize);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != ireader) {
					ireader.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return topDoc;
	}

	/**
	 * 分页查询
	 * @param query
	 * @param pageNum 页码
	 * @param pageSize 每页条数
	 * @param indexPath 索引目录
     * @return
     */
	public static TopDocs searchDocs(Query query, int pageNum, int pageSize, String indexPath){
		DirectoryReader ireader = null;
		Directory directory = null;
		TopDocs topDoc = null;
		try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			ireader = DirectoryReader.open(directory);
			IndexSearcher isearcher = new IndexSearcher(ireader);
			ScoreDoc before = null;
			if(pageNum != 1){
				TopDocs docsBefore = isearcher.search(query, (pageNum-1)*pageSize);
				ScoreDoc[] scoreDocs = docsBefore.scoreDocs;
				if(scoreDocs.length > 0){
					before = scoreDocs[scoreDocs.length - 1];
				}
			}
			topDoc = isearcher.searchAfter(before, query, pageSize);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != ireader) {
					ireader.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return topDoc;
	}

	/**
	 * 根据id查询doc
	 * @param docID
	 * @param indexPath
     * @return
     */
	public static Document searchDoc(int docID, String indexPath){
		Document doc = new Document();
		DirectoryReader ireader = null;
		Directory directory = null;
		try {
			directory = FSDirectory.open(new File(indexPath).toPath());
			ireader = DirectoryReader.open(directory);
			IndexSearcher isearcher = new IndexSearcher(ireader);
			doc = isearcher.doc(docID);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != ireader) {
					ireader.close();
				}
				if (null != directory) {
					directory.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return doc;
	}

	/**
	 * 高亮显示
	 * @param query
	 * @param doc
	 * @param field 高亮的字段
	 * @param preTag 高亮开始标签
	 * @param postTag 高亮结束标签
	 * @param fragmentSize
     * @return
     */
	public static String highlighter(Query query, Document doc, String field, String preTag, String postTag,int fragmentSize){
		QueryScorer scorer = new QueryScorer(query);
		//设定高亮显示的格式<B>keyword</B>,此为默认的格式
		SimpleHTMLFormatter simpleHtmlFormatter = new SimpleHTMLFormatter(preTag, postTag);
		Highlighter highlighter = new Highlighter(simpleHtmlFormatter, scorer);
		//highlighter.setTextFragmenter(new SimpleFragmenter(fragmentSize));//设置每次返回的字符数
		Analyzer analyzer = new IKAnalyzer(true);
		String result = "";
		try {
			result = highlighter.getBestFragment(analyzer, field, doc.get(field));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void main(String[] args) throws ParseException {
		//LuceneUtils.analyzerString("吕绍刚");
		Map<String, String> map = new HashMap<String, String>();
		map.put("title", "深圳发布防台风防汛紧急动员令 全市停工停业停市停课");
		map.put("content", "人民网深圳8月1日电（吕绍刚 夏凡）8月1日，深圳市防台风防汛紧急动员令发布。动员令宣布，从当天17时起，启动全市防台风和防汛Ⅰ级紧急响应。期间，深圳全市范围内实行“四停”，即停工、停业、停市、停课。");
		Document doc = LuceneUtils.createDocument(map);
		LuceneUtils.createIndex(doc, "D:\\lucene", false);

		Map<String, String> param = new HashMap<String, String>();
		//param.put("title", "吕绍刚");
		param.put("content", "吕绍刚");
		Query query = LuceneUtils.createQuery(param);

		TopDocs topDocs = LuceneUtils.searchDocs(query, 1000, "D:\\lucene");
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		System.out.println("scoreDocs:"+scoreDocs.length);
		for (ScoreDoc scoreDoc : scoreDocs) {
			Document d = LuceneUtils.searchDoc(scoreDoc.doc, "D:\\lucene");
			System.out.println("title:"+d.getField("title"));
			System.out.println("content:"+d.getField("content"));
			String result = LuceneUtils.highlighter(query, d, "content", "<font color='red'>", "</font>", 100);
			System.out.println("result:"+result);
		}
	}
}
